/*
* Copyright (c) Microsoft Corporation.  All Rights Reserved.
*
* Public Mock10 API
*/
#pragma once
#include "Mock10.h"

#include "MockFunction.h"

#pragma push_macro("Assert")
#undef Assert

#pragma push_macro("SEALED")
#undef SEALED

#if _MSC_VER >= 1400
# define SEALED sealed
#else
# define SEALED
#endif

namespace Mock10
{
    namespace Private
    {
        class ExternalMockFunctionBase;
    }

    namespace AllowMultipleProcesses
    {
        enum Value
        {
            No,
            Yes
        };
    }

    // Public API used to inject Mock10 into one or more target processes, and to track the lifetime of that injection.  
    // An ExternalMockTarget instance must be passed into any of the Mock::External* APIs.  
    class MOCK10_API ExternalMockTarget SEALED
    {
    friend class Private::ExternalMockFunctionBase;

    public:
        // Inject Mock10 into the specfied process(es) in the current session
        static ExternalMockTarget __stdcall FromProcessName(const wchar_t* pszProcessName, AllowMultipleProcesses::Value allowMultiple = AllowMultipleProcesses::No)
        {
            ExternalMockTarget target = FromProcessNameImpl(pszProcessName, allowMultiple);

            #if !defined(NO_MOCK_THROW)
            if (FAILED(target.GetStatus()))
            {
                Private::Throw::MockException(target.GetStatus(), reinterpret_cast<const wchar_t*>(target.GetStatusMessage()));
            }
            #endif

            return target;
        }

        #if defined(MOCK10_FULL_BUILD)
        static ExternalMockTarget __stdcall FromProcessName(const __wchar_t* pszProcessName, AllowMultipleProcesses::Value allowMultiple = AllowMultipleProcesses::No)
        {
            return FromProcessName(reinterpret_cast<const wchar_t*>(pszProcessName), allowMultiple);
        }
        #endif

        // Inject Mock10 into the specfied process(es) in the specified session; use the value ExternalMockTarget::MockAnySession for the sessionId to inject into any session
        static const unsigned long MockAnySession = static_cast<unsigned long>(-1);
        static ExternalMockTarget __stdcall FromProcessName(const wchar_t* pszProcessName, unsigned long sessionId, AllowMultipleProcesses::Value allowMultiple = AllowMultipleProcesses::No)
        {
            ExternalMockTarget target = FromProcessNameImpl(pszProcessName, sessionId, allowMultiple);

            #if !defined(NO_MOCK_THROW)
            if (FAILED(target.GetStatus()))
            {
                Private::Throw::MockException(target.GetStatus(), reinterpret_cast<const wchar_t*>(target.GetStatusMessage()));
            }
            #endif

            return target;
        }

        #if defined(MOCK10_FULL_BUILD)
        static ExternalMockTarget __stdcall FromProcessName(const __wchar_t* pszProcessName, unsigned long sessionId, AllowMultipleProcesses::Value allowMultiple = AllowMultipleProcesses::No)
        {
            return FromProcessName(reinterpret_cast<const wchar_t*>(pszProcessName), sessionId, allowMultiple);
        }
        #endif

        // Inject Mock10 into the specfied service
        static ExternalMockTarget __stdcall FromServiceName(const wchar_t* pszServiceName)
        {
            ExternalMockTarget target = FromServiceNameImpl(pszServiceName);

            #if !defined(NO_MOCK_THROW)
            if (FAILED(target.GetStatus()))
            {
                Private::Throw::MockException(target.GetStatus(), reinterpret_cast<const wchar_t*>(target.GetStatusMessage()));
            }
            #endif

            return target;
        }

        #if defined(MOCK10_FULL_BUILD)
        static ExternalMockTarget __stdcall FromServiceName(const __wchar_t* pszServiceName)
        {
            return FromServiceName(reinterpret_cast<const wchar_t*>(pszServiceName));
        }
        #endif

        // Inject Mock10 into the specfied process
        static ExternalMockTarget __stdcall FromProcessId(unsigned long processId)
        {
            ExternalMockTarget target = FromProcessIdImpl(processId);

            #if !defined(NO_MOCK_THROW)
            if (FAILED(target.GetStatus()))
            {
                Private::Throw::MockException(target.GetStatus(), reinterpret_cast<const wchar_t*>(target.GetStatusMessage()));
            }
            #endif

            return target;
        }

        HRESULT GetStatus() const;
        const unsigned short* GetStatusMessage() const; // Return unsigned short* here; otherwise we'd need to create both wchar_t* and __wchar_t* versions that return the value as an out parameter

        ExternalMockTarget(const ExternalMockTarget& other);
        ~ExternalMockTarget();

    private:
        ExternalMockTarget();
        ExternalMockTarget& operator=(const ExternalMockTarget&); // Not implemented

        static ExternalMockTarget __stdcall FromProcessNameImpl(const wchar_t* pszProcessName, AllowMultipleProcesses::Value allowMultiple);
        static ExternalMockTarget __stdcall FromProcessNameImpl(const wchar_t* pszProcessName, unsigned long sessionId, AllowMultipleProcesses::Value allowMultiple);
        static ExternalMockTarget __stdcall FromServiceNameImpl(const wchar_t* pszServiceName);
        static ExternalMockTarget __stdcall FromProcessIdImpl(unsigned long processId);

        #if defined(MOCK10_FULL_BUILD)
        static ExternalMockTarget __stdcall FromProcessNameImpl(const __wchar_t* pszProcessName, AllowMultipleProcesses::Value allowMultiple);
        static ExternalMockTarget __stdcall FromProcessNameImpl(const __wchar_t* pszProcessName, unsigned long sessionId, AllowMultipleProcesses::Value allowMultiple);
        static ExternalMockTarget __stdcall FromServiceNameImpl(const __wchar_t* pszServiceName);
        #endif

        struct Impl;
        Impl* m_pImpl;
    };
} /* namespace Mock10 */

#pragma pop_macro("SEALED")
#pragma pop_macro("Assert")
